#include "csv.h"
#include <fstream>
#include <string>

namespace anet {
	namespace utils {
		// static global string 
		std::string CCSVParser::m_staticStr("");

		CCSVParser::CCSVParser() {
			m_vecLines.clear();
			m_field2index.clear();
		}

		CCSVParser::~CCSVParser() {
			m_vecLines.clear();
			m_field2index.clear();
		}

		bool CCSVParser::Load(const char* path, bool has_field_head) {
			if (!path) {
				return false;
			}

			try {
				std::ifstream ifs;
				ifs.open(path);
				if (!ifs) return false;

				int line = 0;
				while (!ifs.eof()) {
					std::string strline;
					getline(ifs, strline);
					if (strline.empty()) {
						continue;
					}
					// parse line
					parseLine(strline, line++ == 0);
				}

				if (has_field_head && !m_vecLines.empty()) {
					// try read line one about field information.
					const CFieldsVec& vecfield = m_vecLines[0];
					for (size_t i = 0; i < vecfield.size(); i++) {
						if (m_field2index.find(vecfield[i]) != m_field2index.end()) {
							// field name is repeated.
							return false;
						} else {
							m_field2index[vecfield[i]] = i;
						}
					}
				}
				ifs.close();

				return true;
			} catch (...) {
				return false;
			}
		}

		void CCSVParser::parseCSVLine(const std::string& line, std::vector<std::string>& fields) {
			std::string field;
			bool inQuotes = false;
			for (char c : line) {
				if (!inQuotes) {
					if (c == ',') {
					    auto f = TrimSpace(field);
						fields.push_back(std::move(f));
						field.clear();
					} else if (c == '"') {
						inQuotes = true;
					} else {
						field += c;
					}
				} else {
					if (c == '"') {
						if (field.empty() || field.back() != '"') {
							inQuotes = false;
						} else {
							field.pop_back();
							field += '"';
						}
					} else {
						field += c;
					}
				}
			}
			fields.push_back(std::move(field));
		}

		void CCSVParser::parseLine(std::string& strLine, bool first) {
			if (first && strLine.size() >= 3) { // check BOM.
				const unsigned char* dst = (const unsigned char*)strLine.c_str();
				const unsigned char src[] = { 0xEF, 0xBB, 0xBF };
				if (dst[0] == src[0] && dst[1] == src[1] && dst[2] == src[2]) {
					strLine = strLine.substr(3); // remove BOM string.
				}
			}

			// exclude special char.
			strLine = TrimSpace(strLine);

			// build fields.
			CFieldsVec vecFields;
			this->parseCSVLine(strLine, vecFields);

			// exclude all ,,,,, form.
			for (size_t i = 0; i < vecFields.size(); i++) {
				if (!vecFields[i].empty()) {
					m_vecLines.push_back(vecFields);
					return;
				}
			}
		}

		size_t CCSVParser::GetLineCount() const {
			return m_vecLines.size();
		}

		size_t CCSVParser::GetFieldCount(size_t line) const {
			if (line >= GetLineCount()) {
				return -1;
			}

			const CFieldsVec& vecFields = m_vecLines[line];
			return vecFields.size();
		}

		const std::string& CCSVParser::GetField(size_t line, size_t field) const {
			if (line >= GetLineCount()) {
				return m_staticStr;
			}

			const CFieldsVec& vecFields = m_vecLines[line];
			if (field >= vecFields.size()) {
				return m_staticStr;
			}
			return vecFields[field];
		}

		const std::string& CCSVParser::GetField(size_t line, const char* field) const {
			auto it = m_field2index.find(field);
			if (it == m_field2index.end()) {
				char b[32] = { 0 };
				std::snprintf(b, sizeof(b), "can not find field %s", field);
				return m_staticStr;
			}
			return this->GetField(line, it->second);
		}

		const std::string& CCSVParser::GetFieldWithoutError(size_t line, const char* field) const {
			auto it = m_field2index.find(field);
			if (it == m_field2index.end()) {
				return m_staticStr;
			}
			return this->GetField(line, it->second);
		}

		const std::string& CCSVParser::GetField(size_t line, size_t field) {
			if (line >= GetLineCount()) {
				return m_staticStr;
			}

			CFieldsVec& vecFields = m_vecLines[line];
			if (field >= vecFields.size()) {
				return m_staticStr;
			}
			return vecFields[field];
		}

		std::string CCSVParser::GetLine(size_t line) const {
			std::string str("");
			if (line >= GetLineCount()) {
				return str;
			}

			const CFieldsVec& vecFields = m_vecLines[line];
			for (size_t i = 0; i < vecFields.size(); i++) {
				const std::string& strContent = vecFields[i];
				if (!strstr(strContent.c_str(), ",")) {
					str += strContent;
				} else {
					str += "\"";
					str += strContent;
					str += "\"";
				}
				str += ",";
			}

			return str;
		}

		void CCSVParser::Write(const char* pszCsvFile) {
			std::ofstream of(pszCsvFile, std::ios::out | std::ios::app);
			for (size_t i = 0; i < m_vecLines.size(); i++) {
#ifdef WIN32
				of << this->GetLine(i) << "\r\n";
#else 
				of << this->GetLine(i) << "\n";
#endif
			}
			of.flush();

			of.close();
		}

		bool CCSVParser::HasLine(size_t line) const {
			return line >= m_vecLines.size();
		}

		bool CCSVParser::HasField(size_t line, size_t field) const {
			if (!HasLine(line)) {
				return false;
			}
			return field >= m_vecLines[line].size();
		}

		bool CCSVParser::HasField(size_t line, const char* field) {
			if (!HasLine(line)) {
				return false;
			}
			return m_field2index.find(field) != m_field2index.end();
		}

		long long CCSVParser::GetFieldInt64(size_t line, const char* field) const {
			return toUserType<long long>(GetField(line, field));
		}

		int CCSVParser::GetFieldInt(size_t line, const char* field) const {
			return toUserType<int>(GetField(line, field));
		}

		unsigned int CCSVParser::GetFieldUint(size_t line, const char* field) const {
			return toUserType<unsigned int>(GetField(line, field));
		}

		double CCSVParser::GetFiledFloat(size_t line, const char* field) const {
			return toUserType<double>(GetField(line, field).c_str());
		}

		bool CCSVParser::GetFieldBool(size_t line, const char* field) const {
			return toUserType<bool>(GetField(line, field).c_str());
		}
	}
}